// Copyright (c) Appeon. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using SnapObjects.Commons;

namespace SnapObjects.Expressions
{
    internal class EvaluateMethodMatcher : MethodMatcher<EvaluateResult, ParameterInfo>
    {
        protected readonly IMetadataManager<EvaluateResult> _metadatas;

        public EvaluateMethodMatcher(IMetadataManager<EvaluateResult> metadatas)
        {
            _metadatas = metadatas;
        }

        protected override EvaluateResult CreateDefault(Type type, object defaultValue)
        {
            try
            {
                object value = null;

                try
                {
                    value = Convert.ChangeType(defaultValue, type);
                }
                catch (Exception) when (type.IsNullable())
                {
                    var realType = Nullable.GetUnderlyingType(type);

                    value = Convert.ChangeType(defaultValue, realType);
                }

                return new EvaluateResult(value, type);
            }
            catch (Exception)
            {
                var constructors = type.GetConstructors();

                if (constructors.Length > 0)
                {
                    var arrs = Activator.CreateInstance(type);

                    return new EvaluateResult(arrs, type);
                }
            }

            return new EvaluateResult(null, type);
        }

        protected override EvaluateResult CreateParams(Type type, List<EvaluateResult> initializers)
        {
            var paramters = Array.CreateInstance(type, initializers.Count);

            Array.Copy(initializers.Select(x => x.Value).ToArray(), paramters, initializers.Count);

            return new EvaluateResult(paramters, type);
        }

        protected override EvaluateResult ConvertType(EvaluateResult argExpr, Type type)
        {
            try
            {
                return new EvaluateResult(Utils.ChangeType(argExpr.Value, type), type);
            }
            catch (Exception)
            {
                return null;
            }
        }

        protected override Type GetArgExprType(EvaluateResult argExpr)
        {
            return argExpr.Type;
        }

        protected override Type GetParamExprType(ParameterInfo paramExpr)
        {
            return paramExpr.ParameterType;
        }

        protected override EvaluateResult CharToString(EvaluateResult argExpr)
        {
            return new EvaluateResult(argExpr.Value.ToString(), typeof(string));
        }

        protected override bool MatchDefaultParameter(
            ParserOptions<EvaluateResult> options, ParameterInfo paramExpr, out EvaluateResult outExp)
        {
            if (paramExpr.ParameterType.IsInterface)
            {
                var isOk = _metadatas.TryGetMetadata(
                    MetadataType.Parameter, paramExpr.ParameterType, options, out var metadata);

                if (isOk && metadata is ParameterMetadata<EvaluateResult> parameter)
                {
                    outExp = parameter.Value;
                }
                else
                {
                    outExp = null;
                    return false;
                }

                return true;
            }

            outExp = null;
            return false;
        }
    }
}
